﻿/**
 ******************************************************************************
 * @file           : bsp_spi.c
 * @brief          : SPI Driver Implementation (STM32F4xx)
 ******************************************************************************
 * @attention
 *
 * SPI驱动程序实现文件
 * 提供SPI主机模式通信功能
 * 支持阻塞式收发和完整的错误处理
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/SPI/bsp_spi.h"
#include "./BSP/SPI/bsp_spi_config.h"

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static SPI_HandleTypeDef sg_spi_handle;                     /*!< SPI句柄 */
static volatile SPI_State_e sg_spi_state = SPI_STATE_IDLE;  /*!< SPI当前状态 */
static volatile uint8_t sg_spi_initialized = 0;             /*!< 初始化标志 */

#if (SPI_USE_DMA == 1)
static DMA_HandleTypeDef sg_spi_dma_tx_handle;              /*!< SPI DMA TX句柄 */
static volatile uint8_t sg_dma_transfer_done = 1;           /*!< DMA传输完成标志 */
#endif

/* Private function prototypes -----------------------------------------------*/
static void spi_gpio_init(void);
static void spi_peripheral_init(void);

#if (SPI_USE_DMA == 1)
static void spi_dma_init(void);
#endif

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       SPI GPIO初始化（STM32F4xx版本）
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置SCK、MISO、MOSI、CS引脚，使用GPIO AF复用功能
 */
static void spi_gpio_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;

    /* 使能GPIO时钟 */
    SPI_SCK_GPIO_CLK_ENABLE();
    SPI_MOSI_GPIO_CLK_ENABLE();
    SPI_CS_GPIO_CLK_ENABLE();
    
#if (SPI_DIRECTION_MODE != 1)
    /* 双线模式才需要MISO */
    SPI_MISO_GPIO_CLK_ENABLE();
#endif

    /* 配置SCK引脚 - 复用推挽输出 + AF功能 */
    gpio_init_struct.Pin = SPI_SCK_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;
    gpio_init_struct.Pull = GPIO_NOPULL;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    gpio_init_struct.Alternate = SPI_SCK_AF;                    /* STM32F4xx需要指定AF功能 */
    HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &gpio_init_struct);

#if (SPI_DIRECTION_MODE != 1)
    /* 配置MISO引脚 - 复用推挽输出 + AF功能（仅双线模式） */
    gpio_init_struct.Pin = SPI_MISO_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;
    gpio_init_struct.Pull = GPIO_NOPULL;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    gpio_init_struct.Alternate = SPI_MISO_AF;                   /* STM32F4xx需要指定AF功能 */
    HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &gpio_init_struct);
#endif

    /* 配置MOSI引脚 - 复用推挽输出 + AF功能 */
    gpio_init_struct.Pin = SPI_MOSI_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_AF_PP;
    gpio_init_struct.Pull = GPIO_NOPULL;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    gpio_init_struct.Alternate = SPI_MOSI_AF;                   /* STM32F4xx需要指定AF功能 */
    HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &gpio_init_struct);

    /* 配置CS引脚 - 通用推挽输出 */
    gpio_init_struct.Pin = SPI_CS_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_PULLUP;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(SPI_CS_GPIO_PORT, &gpio_init_struct);

    /* CS默认拉高（未选中） */
    SPI_CS_HIGH();
}

/**
 * @brief       SPI外设初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置SPI工作模式和参数
 */
static void spi_peripheral_init(void)
{
    /* 使能SPI时钟 */
    SPI_SPIx_CLK_ENABLE();

    /* 配置SPI参数 */
    sg_spi_handle.Instance = SPI_SPIx;
    sg_spi_handle.Init.Mode = SPI_MODE_MASTER;                      /* 主机模式 */
    sg_spi_handle.Init.Direction = SPI_DIRECTION;                   /* 方向模式（单线/双线）*/
    sg_spi_handle.Init.DataSize = SPI_DATASIZE;                     /* 数据位宽 */
    sg_spi_handle.Init.CLKPolarity = SPI_CPOL;                      /* 时钟极性 */
    sg_spi_handle.Init.CLKPhase = SPI_CPHA;                         /* 时钟相位 */
    sg_spi_handle.Init.NSS = SPI_NSS_SOFT;                          /* 软件控制CS */
    sg_spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATE_PRESCALER;  /* 波特率分频 */
    sg_spi_handle.Init.FirstBit = SPI_FIRST_BIT;                    /* 传输顺序 */
    sg_spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;                 /* 禁用TI模式 */
    sg_spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; /* 禁用CRC */

    /* 初始化SPI */
    if (HAL_SPI_Init(&sg_spi_handle) != HAL_OK)
    {
        log_e("SPI外设初始化失败");
    }
}

#if (SPI_USE_DMA == 1)
/**
 * @brief       SPI DMA初始化（STM32F4xx版本）
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置DMA用于SPI发送，STM32F4xx使用DMA Stream
 */
static void spi_dma_init(void)
{
    /* 使能DMA时钟 */
    SPI_DMA_TX_CLK_ENABLE();

    /* 配置DMA TX Stream */
    sg_spi_dma_tx_handle.Instance = SPI_DMA_TX_STREAM;
    sg_spi_dma_tx_handle.Init.Channel = SPI_DMA_TX_CHANNEL;            /* DMA通道 */
    sg_spi_dma_tx_handle.Init.Direction = DMA_MEMORY_TO_PERIPH;        /* 内存到外设 */
    sg_spi_dma_tx_handle.Init.PeriphInc = DMA_PINC_DISABLE;            /* 外设地址不递增 */
    sg_spi_dma_tx_handle.Init.MemInc = DMA_MINC_ENABLE;                /* 内存地址递增 */
    sg_spi_dma_tx_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; /* 外设数据宽度8位 */
    sg_spi_dma_tx_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;  /* 内存数据宽度8位 */
    sg_spi_dma_tx_handle.Init.Mode = DMA_NORMAL;                       /* 正常模式 */
    sg_spi_dma_tx_handle.Init.Priority = DMA_PRIORITY_HIGH;            /* 高优先级 */
    sg_spi_dma_tx_handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;         /* 禁用FIFO */

    /* 初始化DMA */
    if (HAL_DMA_Init(&sg_spi_dma_tx_handle) != HAL_OK)
    {
        log_e("SPI DMA初始化失败");
        return;
    }

    /* 关联DMA和SPI */
    __HAL_LINKDMA(&sg_spi_handle, hdmatx, sg_spi_dma_tx_handle);

    /* 配置DMA中断优先级并使能 */
    HAL_NVIC_SetPriority(SPI_DMA_TX_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(SPI_DMA_TX_IRQn);

    log_d("SPI DMA初始化完成");
}
#endif

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       SPI初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        初始化SPI GPIO和外设
 */
void spi_init(void)
{
    if (sg_spi_initialized)
    {
        log_w("SPI已经初始化");
        return;
    }

    /* 初始化GPIO */
    spi_gpio_init();

    /* 初始化SPI外设 */
    spi_peripheral_init();

#if (SPI_USE_DMA == 1)
    /* 初始化DMA */
    spi_dma_init();
#endif

    /* 设置状态 */
    sg_spi_state = SPI_STATE_IDLE;
    sg_spi_initialized = 1;

    log_i("SPI初始化完成");
}

/**
 * @brief       SPI反初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        反初始化SPI外设和GPIO
 */
void spi_deinit(void)
{
    if (!sg_spi_initialized)
    {
        log_w("SPI未初始化");
        return;
    }

    /* 反初始化SPI */
    HAL_SPI_DeInit(&sg_spi_handle);

#if (SPI_USE_DMA == 1)
    /* 反初始化DMA */
    HAL_DMA_DeInit(&sg_spi_dma_tx_handle);
#endif

    /* 重置状态 */
    sg_spi_state = SPI_STATE_IDLE;
    sg_spi_initialized = 0;

    log_i("SPI反初始化完成");
}

/**
 * @brief       选中从机（拉低CS）
 *
 * @param       无
 *
 * @retval      无
 */
void spi_cs_select(void)
{
    SPI_CS_LOW();
}

/**
 * @brief       取消选中（拉高CS）
 *
 * @param       无
 *
 * @retval      无
 */
void spi_cs_deselect(void)
{
    SPI_CS_HIGH();
}

/**
 * @brief       读写单字节
 *
 * @param[in]   tx_data - 发送数据
 *
 * @retval      接收数据字节
 */
uint8_t spi_read_write_byte(uint8_t tx_data)
{
    uint8_t rx_data = 0;

    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return 0;
    }

    sg_spi_state = SPI_STATE_TX_RX;

    if (HAL_SPI_TransmitReceive(&sg_spi_handle, &tx_data, &rx_data, 1, SPI_TIMEOUT_MS) != HAL_OK)
    {
        log_e("SPI读写字节失败");
        sg_spi_state = SPI_STATE_ERROR;
        return 0;
    }

    sg_spi_state = SPI_STATE_IDLE;
    return rx_data;
}

/**
 * @brief       写多字节
 *
 * @param[in]   data - 指向数据缓冲区的指针
 * @param[in]   len  - 要写入的字节数
 *
 * @retval      成功返回SPI_OK，否则返回错误码
 */
SPI_Error_e spi_write(const uint8_t *data, uint16_t len)
{
    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return SPI_ERROR_PARAM;
    }

    if (data == NULL || len == 0)
    {
        log_e("无效的写入参数");
        return SPI_ERROR_PARAM;
    }

    sg_spi_state = SPI_STATE_TX;

    if (HAL_SPI_Transmit(&sg_spi_handle, (uint8_t *)data, len, SPI_TIMEOUT_MS) != HAL_OK)
    {
        log_e("SPI写入失败");
        sg_spi_state = SPI_STATE_ERROR;
        return SPI_ERROR_HAL;
    }

    sg_spi_state = SPI_STATE_IDLE;
    return SPI_OK;
}

/**
 * @brief       读多字节
 *
 * @param[out]  data - 指向数据缓冲区的指针
 * @param[in]   len  - 要读取的字节数
 *
 * @retval      成功返回SPI_OK，否则返回错误码
 */
SPI_Error_e spi_read(uint8_t *data, uint16_t len)
{
    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return SPI_ERROR_PARAM;
    }

    if (data == NULL || len == 0)
    {
        log_e("无效的读取参数");
        return SPI_ERROR_PARAM;
    }

    sg_spi_state = SPI_STATE_RX;

    if (HAL_SPI_Receive(&sg_spi_handle, data, len, SPI_TIMEOUT_MS) != HAL_OK)
    {
        log_e("SPI读取失败");
        sg_spi_state = SPI_STATE_ERROR;
        return SPI_ERROR_HAL;
    }

    sg_spi_state = SPI_STATE_IDLE;
    return SPI_OK;
}

/**
 * @brief       全双工收发（同时读写）
 *
 * @param[in]   tx_data - 指向发送数据缓冲区的指针
 * @param[out]  rx_data - 指向接收数据缓冲区的指针
 * @param[in]   len     - 要收发的字节数
 *
 * @retval      成功返回SPI_OK，否则返回错误码
 */
SPI_Error_e spi_write_read(const uint8_t *tx_data, uint8_t *rx_data, uint16_t len)
{
    if (!sg_spi_initialized)
    {
        log_e("SPI未初始化");
        return SPI_ERROR_PARAM;
    }

    if (tx_data == NULL || rx_data == NULL || len == 0)
    {
        log_e("无效的收发参数");
        return SPI_ERROR_PARAM;
    }

    sg_spi_state = SPI_STATE_TX_RX;

    if (HAL_SPI_TransmitReceive(&sg_spi_handle, (uint8_t *)tx_data, rx_data, len, SPI_TIMEOUT_MS) != HAL_OK)
    {
        log_e("SPI收发失败");
        sg_spi_state = SPI_STATE_ERROR;
        return SPI_ERROR_HAL;
    }

    sg_spi_state = SPI_STATE_IDLE;
    return SPI_OK;
}

/**
 * @brief       获取SPI状态
 *
 * @param       无
 *
 * @retval      当前SPI状态
 */
SPI_State_e spi_get_state(void)
{
    return sg_spi_state;
}

/**
 * @brief       检查SPI是否忙
 *
 * @param       无
 *
 * @retval      忙返回1，空闲返回0
 */
uint8_t spi_is_busy(void)
{
    return (sg_spi_state != SPI_STATE_IDLE) ? 1 : 0;
}

/**
 * @brief       打印SPI状态（调试接口）
 *
 * @param       无
 *
 * @retval      无
 */
void spi_print_status(void)
{
    const char *state_str[] = {
        "空闲",
        "忙",
        "发送",
        "接收",
        "收发",
        "错误"
    };

    if (sg_spi_state < 6)
    {
        log_i("SPI状态: %s", state_str[sg_spi_state]);
    }
    else
    {
        log_i("SPI状态: 未知");
    }

    log_i("SPI初始化: %d", sg_spi_initialized);
}

#if (SPI_USE_DMA == 1)
/**
 * @brief       DMA中断处理函数（供stm32f4xx_it.c调用）
 *
 * @param       无
 *
 * @retval      无
 */
void SPI_DMA_IRQHandler(void)
{
    HAL_DMA_IRQHandler(&sg_spi_dma_tx_handle);
    sg_dma_transfer_done = 1;
    sg_spi_state = SPI_STATE_IDLE;
}
#endif
